#include "instructionSet.h"

int (*instructionPtr[]) (Command * cmd) = 
			{add,sub,mul,division,addi,subi,lw,sw,beq,bne,slt,slti,j,halt};


char jumpToLabel[MAX_LABEL_LENGTH] = "";
int isHalt = 0;


int isLegalAddress(int address) {
	if (address % 4 != 0) // is address aligned?
		return 0;
	// is address out of bounds?
	if (address >= INSTURCTIONS_OFFSET || address < 0) return 0;

	return 1;
}

/* Arithmetic */
int add(Command * cmd) {
	if (cmd->rd == 0) return 1; // if target is $0 then it stays the same
	registers[cmd->rd].value = registers[cmd->rs].value+registers[cmd->rt].value;
	
	return 1;
}

int sub(Command * cmd) {
	if (cmd->rd == 0) return 1; // if target is $0 then it stays the same
	registers[cmd->rd].value = registers[cmd->rs].value-registers[cmd->rt].value;
	
	return 1;
}

int mul(Command * cmd) {
	if (cmd->rd == 0) return 1; // if target is $0 then it stays the same
	registers[cmd->rd].value = registers[cmd->rs].value*registers[cmd->rt].value;
	
	return 1;
}

int division(Command * cmd) {
	if (registers[cmd->rt].value == 0) return -1;
	if (cmd->rd == 0) return 1; // if target is $0 then it stays the same
	registers[cmd->rd].value = registers[cmd->rs].value/registers[cmd->rt].value;
	
	return 1;
}

int addi(Command * cmd) {
	if (cmd->rt == 0) return 1; // if target is $0 then it stays the same
	registers[cmd->rt].value = registers[cmd->rs].value+cmd->imm;
	
	return 1;
}

int subi(Command * cmd) {
	if (cmd->rt == 0) return 1; // if target is $0 then it stays the same
	registers[cmd->rt].value = registers[cmd->rs].value-cmd->imm;
	
	return 1;
}

/* Data Transfer */
int lw(Command * cmd) {
	int tmp_register,i, address;
	unsigned char * buffer;

	address = cmd->offset + registers[cmd->rs].value;

	if (!isLegalAddress(address))
		return -1;

	buffer = (unsigned char *) malloc (NUM_BYTES_IN_WORD * sizeof(unsigned char));

	fetchDataFromCache(buffer,address);

	if (cmd->rt == 0) return 1; // if target is $0 then it stays the same

	tmp_register = 0;

	for (i = 0 ; i < NUM_BYTES_IN_WORD ; i++) {
		tmp_register ^= buffer[i] << 8*i;
	}

	registers[cmd->rt].value = tmp_register;

	return 1;
}

int sw(Command * cmd) {
	int mask = 0xFF, i, address;
	unsigned char * buffer;


	address = cmd->offset+registers[cmd->rs].value;
	buffer = (unsigned char *)malloc(NUM_BYTES_IN_WORD*sizeof(unsigned char));

	//printf("store to address: 0x%.8X\n",cmd->command_str,address);

	if (!isLegalAddress(address))
		return -1;

	for (i = 0; i < NUM_BYTES_IN_WORD ; i++) {
		buffer[i] = (registers[cmd->rt].value&(mask << i*8)) >> i*8;
	}

	storeDataToCache(buffer, address);

	if (address >= memory_size)
		memory_size = address - address%8 + 8;
	
	free(buffer);

	return 1;
}

/* Conditional Branch */
int beq(Command * cmd) {
	if (registers[cmd->rs].value == registers[cmd->rt].value) {
		strcpy(jumpToLabel,cmd->argv[2]);
		return 1; /* branch taken */
	} else 
		return 0; /* branch not taken */
}

int bne(Command * cmd) {
	if (registers[cmd->rs].value != registers[cmd->rt].value) {
		strcpy(jumpToLabel,cmd->argv[2]);
		return 1; /* branch taken */
	} else {
		return 0; /* branch not taken */
	}
}

int slt(Command * cmd) {
	if (cmd->rd == 0) return 1; // if target is $0 then it stays the same
	registers[cmd->rd].value = (registers[cmd->rs].value < registers[cmd->rt].value);

	return 1;
}

int slti(Command * cmd) {
	if (cmd->rt == 0) return 1; // if target is $0 then it stays the same
	registers[cmd->rt].value = (registers[cmd->rs].value < cmd->imm);

	return 1;
}

/* Unconditional Jump */
int j(Command * cmd) {
	strcpy(jumpToLabel,cmd->label);
	return 1;
}

/* Other */
int halt(Command * cmd) {
	isHalt = 1;
	return 1;
}